#ifndef DATAPROXY_H
#define	DATAPROXY_H

#include <cppdb/frontend.h>
#include <vector>
#include <sstream>
#include <boost/shared_ptr.hpp>
#include "Structures.hpp"
#include <boost/thread.hpp>

namespace netspy {
    namespace db {
/**
         * Klasa umożliwiająca komunikację z bazą danych w zakresie określonym specyfiką aplikacji.
         * Pozwala uzyskać informacje o wszystkich klientach (komputerach), usługach oraz potencjalnych
         * odpowiedziach zwracanych przez klientów.
         */
        class DataProxy {
        public:

            /**
             * Zwraca shared_ptr do obiektu pośrednika bazodanowego
             * @return shared_ptr<DataProxy>
             */
            static boost::shared_ptr<DataProxy> getInstance();
            
            virtual ~DataProxy();

            /**
             * Pozwala pobrać informację o wszystkich odpowiedziach zdefiniowanych w bazie danych.
             * @return wskaźnik do wektora wskaźników na obiekty struktury Reply
             */
            boost::shared_ptr<const std::vector<boost::shared_ptr<Reply> > > getReplies() const;
          
            /**
             *  Pozwala pobrać informację o zarejestrowanych klientach 
             *  @return wskaźnik na wektor wskaźników na obiekty struktury Client
             */
            boost::shared_ptr<const std::vector<boost::shared_ptr<Client> > > getClients() const;
            
            /**
             * Pozwala pobrać informację o kliencie identyfikowanym numerem id
             * @param id numer id z bazy danych
             * @return wskaźnik na obiekt struktury Client
             */
            boost::shared_ptr<Client> getClient(unsigned int id) const;

            /**
             * Pozwala pobrać informację o kliencje identyfikowanym jego hashem
             * @param hash hash przypisany klientowi
             * @return wskaźnik do struktury Client
             */
            boost::shared_ptr<Client> getClient(std::string hash) const;

            /**
             * Pozwala pobrać informację o wszystkich usługach, pod kątem których odpytywani są klienci
             * @return Wskaźnik na wektor wskaźników na obiekty struktury Service
             */
            boost::shared_ptr<const std::vector<boost::shared_ptr<Service> > > getServices() const;

            /**
             * Pozwala pobrać informację o wybranej, określonej numerem id usłudze.
             * @param id numer id przypisany danej usłudze
             * @return wskaźnik na obiekt struktury Service
             */
            boost::shared_ptr<Service> getService(unsigned int id) const;
            
             /**
             * Pozwala pobrać informację o usługach o podanym opisie
             * @param description opis usług zapisany w bazie danych
             * @return wskaźnik na wektor wskaźników do obiektów struktury Service
             */
            boost::shared_ptr<const std::vector<boost::shared_ptr<Service> > > getServices(std::string description) const;
            
            /**
             * Pozwala pobrać informację o aktywnych usługach świadczonych przez danego klienta.
             * @param hash hash identyfikujący klienta
             * @return wskaźnik na wektor wskaźników do obiektów struktury Service
             */
            boost::shared_ptr<const std::vector<boost::shared_ptr<Service> > > getServicesForClient(std::string hash) const;

            /**
             * Pozwala pobrać informację o aktywnych usługach świadczonych przez danego klienta.
             * @param id numer id klienta zapisany w bazie danych
             * @return wskaźnik na wektor wskaźników do obiektów struktury Service
             */
            boost::shared_ptr<const std::vector<boost::shared_ptr<Service> > > getServicesForClient(unsigned int id) const;

            /**
             * Pozwala skojarzyć klienta z określoną usługą lub zmienić status usługi już klientowi przypisanyej
             * @param client_id id klienta
             * @param service_id id usługi
             * @param enable prawda, jeśli chcemy, aby usługa była aktywowana
             * @return 
             */
            void setClientService(unsigned int client_id, unsigned int service_id, bool enable);

            /**
             * Pozwala skojarzyć klienta z określoną usługą lub zmienić status usługi już klientowi przypisanyej
             * @param client_hash hash klienta
             * @param service_id id usługi
             * @param enable prawda, jeśli chcemy, aby usługa była aktywowana
             * @return 
             */
            void setClientService(std::string client_hash, unsigned int service_id, bool enable);

            /**
             * Pozwala dodać do bazy danych elementarny zapis o dostępności określonej usługi na określonym komputerze
             * @param client_id id klienta
             * @param service_id id usługi
             * @param reply_id kod odpowiedzi
             * @return 
             */
            void addRecord(unsigned int client_id, unsigned int service_id, unsigned int reply_id);

            /**
             * Pozwala dodać do bazy danych elementarny zapis o dostępności określonej usługi na określonym komputerze
             * @param client_hash hash klienta
             * @param service_id numer id klienta
             * @param reply_id kod odpowiedzi
             * @return 
             */
            void addRecord(std::string client_hash, unsigned int service_id, unsigned int reply_id);
            
            /**
             * Usuwa wszystkie przechowywane informacje o danym kliencie
             * @param client_id
             */
            void deleteClientsData(int client_id);
            
            /**
             * Pozwala wprowadzić nową usługę do bazy, o ile usługa o podanym opisie nie zostanie znaleziona.
             * W obu przypadkach zwraca id usługi.
             * @param description opis nowo dodawanej usługi
             * @return 
             */
            int addService(std::string description);
                
            
            /**
             * Opakowuje procedurę rejestracji klienta w bazie danych. Dla nowych klientów dodaje wpis i zwraca id klienta,
             * zaś w przypadku natrafienia na klienta o podanym hashu - zwraca jego id z bazy.
             * @param ip
             * @param port
             * @param hash hash klienta
             * @param remarks opis, komentarz
             * @return przydzielony nowemu klientowi numer id. -1 - błąd
             */
            unsigned int addClient(std::string ip, unsigned short port, std::string hash, std::string remarks);
            
            
        private:
            static boost::shared_ptr<DataProxy> instance;
            static boost::mutex singletonWatchdog;
            
            /**
             * 
             * @param database nazwa bazy danych
             * @param user nazwa użytkownika
             * @param pass hasło dostępu do bazy danych
             */
            DataProxy(std::string database, std::string user, std::string pass);
            DataProxy(const DataProxy& orig);
            
            cppdb::session db;
            std::stringstream queryString;
            boost::shared_ptr<Service> resToService(boost::shared_ptr<cppdb::result> res) const;
            boost::shared_ptr<const std::vector<boost::shared_ptr<Service> > > resToServices(boost::shared_ptr<cppdb::result> res) const;
            boost::shared_ptr<const std::vector<boost::shared_ptr<Reply> > > resToReplies(boost::shared_ptr<cppdb::result> res) const;
            boost::shared_ptr<Client> resToClient(boost::shared_ptr<cppdb::result> res) const;
            boost::shared_ptr<const std::vector<boost::shared_ptr<Client> > > resToClients(boost::shared_ptr<cppdb::result> res) const;
            boost::shared_ptr<cppdb::result> query(std::string table) const;
            boost::shared_ptr<cppdb::result> query(std::string table, std::string column, std::string value) const;

            void resetQuery(){
                queryString.clear();
                queryString.str("");
            }
        };
    }
}
#endif	/* DATAPROXY_H */

